﻿using System;
using System.Configuration;
using System.Text;
using System.Web;
using System.Xml;

namespace RakutenProxy.AttributeParser
{
    public class RakutenProductAttributeParser : IProductAttributeParser
    {
        public RakutenProductAttributeParser(string xmlContent)
        {
            this.Content = xmlContent;
        }

        public RakutenProductAttributeParser(XmlNode xmlContent)
        {
            this.XmlContent = xmlContent;
        }

        public string Content
        {
            get;
            set;
        }

        private XmlNode _xmlContent;
        protected XmlNode XmlContent
        {
            get
            {
                if (_xmlContent == null && !string.IsNullOrEmpty(Content))
                {
                    try
                    {
                        var xmlDoc = new XmlDocument();
                        xmlDoc.LoadXml(Content);

                        _xmlContent = xmlDoc.FirstChild;

                        if (_xmlContent.Name != "table")
                            throw new InvalidOperationException("Not appropriate element");
                    }
                    catch (InvalidOperationException)
                    {
                        _xmlContent = null;
                        throw;
                    }
                    catch { }
                }

                return _xmlContent;
            }
            private set { _xmlContent = value; }
        }

        public AttributeCombination GetProductAttributes()
        {
            var attributeCombination = new AttributeCombination();

            var allRowNodes = XmlContent.SelectNodes("./form/tr");
            if (allRowNodes == null)
                return attributeCombination;

            // Don't have any attribute
            if (allRowNodes.Count <= 2)
                return attributeCombination;

            // Get the attribute names (if any)
            var attributeNameNode = XmlContent.SelectSingleNode("./form/tr[2]");
            var titleNodes = attributeNameNode.SelectNodes(".//span[@class='inventory_title']");

            for (var i = 0; i < titleNodes.Count; ++i)
            {
                XmlNode titleNode = titleNodes[i];
                var value = titleNode.InnerText.Trim();
                value = HttpUtility.HtmlDecode(value);
                if (value == "×")
                    continue;

                var attribute = new Attribute { AttributeName = value };
                if (i == 0)
                    attributeCombination.AttributeX = attribute;
                else if (i == 2)
                    attributeCombination.AttributeY = attribute;
                else
                {
                    attributeCombination.OtherAttributes.Add(attribute);
                }
            }

            // Get attribute values
            var valueTableNode = XmlContent.SelectSingleNode("./form/tr[3]//table//table");
            if (valueTableNode != null)
            {
                if (attributeCombination.AttributeX != null)
                {
                    var firstRowNode = valueTableNode.SelectSingleNode("./tr[1]");
                    var columnNodes = firstRowNode.SelectNodes("./td[@class='inventory_choice_name']");

                    var attributeX = attributeCombination.AttributeX;
                    foreach (XmlNode columnNode in columnNodes)
                    {
                        var valueName = columnNode.InnerText.Trim();
                        if (valueName == "&nbsp;" || string.IsNullOrEmpty(valueName))
                            continue;
                        attributeX.AttributeValues.Add(new AttributeValue { Name = valueName });
                    }
                }

                if (attributeCombination.AttributeY != null)
                {
                    var rowNodes = valueTableNode.SelectNodes("./tr[position() > 1]");

                    var attributeY = attributeCombination.AttributeY;
                    for (var i = 0; i < rowNodes.Count; ++i)
                    {
                        XmlNode rowNode = rowNodes[i];
                        var spanNode = rowNode.SelectSingleNode("./td/span[@class='inventory_choice_name']");
                        if (spanNode != null)
                        {
                            attributeY.AttributeValues.Add(new AttributeValue { Name = spanNode.InnerText.Trim() });
                        }
                    }
                }

                attributeCombination.InitCombinationArray();
                var combinationArray = attributeCombination.AvailableStatuses;

                var inventoryRows = valueTableNode.SelectNodes("./tr[position() > 1]");
                for (int i = 0; i < inventoryRows.Count; i++)
                {
                    var rowNode = inventoryRows[i];
                    var inventoryColumns = rowNode.SelectNodes("./td[@class='inventory']");
                    for (int j = 0; j < inventoryColumns.Count; j++)
                    {
                        var columnNode = inventoryColumns[j];
                        var inputNode = columnNode.SelectSingleNode(".//input");

                        if (inputNode != null)
                        {
                            combinationArray[i, j] = new AttributeValue { ActualValue = inputNode.SelectSingleNode("./@value").Value, FormControlName = inputNode.SelectSingleNode("./@name").Value };
                        }
                    }
                }
            }

            var otherPropertiesQuery = string.Format("./form/tr[position() > {0} and position() < {1}]", attributeCombination.AvailableStatuses != null ? 3 : 1, allRowNodes.Count);
            var otherRowNodes = XmlContent.SelectNodes(otherPropertiesQuery);
            foreach (XmlNode otherRowNode in otherRowNodes)
            {
                var choiceNameNodes = otherRowNode.SelectNodes(".//span[@class='choice']");
                var selectNodes = otherRowNode.SelectNodes(".//select[@name='choice']");

                for (int i = 0; i < selectNodes.Count; i++)
                {
                    var choiceNameNode = choiceNameNodes[i];
                    var selectNode = selectNodes[i];
                    var attribute = new Attribute { AttributeName = choiceNameNode.InnerText.Trim(), HtmlName = "choice" };
                    var optionNodes = selectNode.SelectNodes("./option");

                    foreach (XmlNode optionNode in optionNodes)
                    {
                        var name = optionNode.InnerText.Trim();
                        var actualValue = optionNode.SelectSingleNode("./@value").Value;
                        var attributeValue = new AttributeValue { Name = name, ActualValue = actualValue };
                        attribute.AttributeValues.Add(attributeValue);
                    }

                    attributeCombination.OtherAttributes.Add(attribute);
                }
            }

            return attributeCombination;
        }

        public FormData ExtractFormData(XmlNode formNode, XmlNamespaceManager xmlns)
        {
            var formData = new FormData();

            var appSettings = ConfigurationManager.AppSettings;
            formData.DestinationUrl = formNode.SelectSingleNode("./@action", xmlns).Value;

            if (formData.DestinationUrl[0] == '/')
            {
                formData.DestinationUrl = "https://basket.step.rakuten.co.jp" + formData.DestinationUrl;
            }
            var inputNodes = formNode.SelectNodes(".//input", xmlns);
            foreach (XmlNode inputNode in inputNodes)
            {
                var type = inputNode.SelectSingleNode("./@type", xmlns).Value.ToLower();
                if (type == "image")
                    continue;

                string key;
                var keyNode = inputNode.SelectSingleNode("./@name");
                if (keyNode != null)
                    key = keyNode.Value;
                else
                    continue;

                var value = inputNode.SelectSingleNode("./@value").Value;

                if ((type == "radio" || type == "checkbox") && formData.ContainsKey(key))
                    continue;
                formData.Add(key, value);
            }

            var selectNodes = formNode.SelectNodes(".//select", xmlns);
            foreach (XmlNode selectNode in selectNodes)
            {
                var name = selectNode.SelectSingleNode("./@name", xmlns).Value;
                var options = selectNode.SelectNodes(".//option");
                bool hasSelected = false;
                foreach (XmlNode option in options)
                {
                    if (option.Attributes["selected"] != null)
                    {
                        var value = option.SelectSingleNode("./@value", xmlns).Value;
                        if (value != "--")
                        {
                            formData.Add(name, option.SelectSingleNode("./@value", xmlns).Value);
                            hasSelected = true;
                            break;
                        }
                    }
                }

                if (!hasSelected)
                {
                    XmlNode option = null;
                    if (options.Count > 1)
                        option = options[1];
                    else
                        option = options[0];
                    var value = option.SelectSingleNode("./@value", xmlns).Value;
                    formData.Add(name, value);
                }
            }

            var textAreaNodes = formNode.SelectNodes(".//textarea", xmlns);
            foreach (XmlNode textAreaNode in textAreaNodes)
            {
                var key = textAreaNode.SelectSingleNode("./@name").Value;
                var value = textAreaNode.InnerXml;
                if (!string.IsNullOrEmpty(value))
                    formData.Add(key, value);
            }

            return formData;
        }

        public string GetPurchaseFormDataFormat()
        {
            var document = XmlContent.OwnerDocument;
            var xmlnsmng = new XmlNamespaceManager(document.NameTable);
            var formNode = XmlContent.SelectSingleNode(".//form");
            var formData = ExtractFormData(formNode, xmlnsmng);
            return formData.ToString();
        }

        public string GetAllowedQuantities()
        {
            var unitInputNode = XmlContent.SelectSingleNode(".//input[@id='units']");
            if (unitInputNode != null)
                return null;
            else
            {
                var unitSelectNode = XmlContent.SelectSingleNode(".//select[@name='units']");
                if (unitSelectNode != null)
                {
                    var options = unitSelectNode.SelectNodes("./option");
                    var builder = new StringBuilder();

                    foreach (XmlNode option in options)
                    {
                        var value = option.SelectSingleNode("./@value");
                        if (value != null)
                        {
                            builder.AppendFormat("{0},", value.Value);
                        }
                    }

                    builder = builder.Remove(builder.Length - 1, 1);
                    return builder.ToString();
                }
            }

            return null;
        }

        public string GetPurchaseActionUrl()
        {
            if (XmlContent == null)
                return string.Empty;

            var formNode = XmlContent.SelectSingleNode(".//form/@action");
            if (formNode != null)
                return formNode.Value;

            return string.Empty;
        }
    }
}